#pragma once

namespace Melting
{
namespace Parallel
{
//#define WIN32_MUTEX_IMPL_OLD_MUTEX			1
#define WIN32_MUTEX_IMPL_CRITICAL_SECTION		1
//#define WIN32_MUTEX_IMPL_SWRLOCK				1

	//--------------------------------------------------------------------------------------------------------------------
	
#ifdef WIN32
	#ifdef WIN32_MUTEX_IMPL_OLD_MUTEX
		typedef HANDLE				MutexHandle;
	#endif
	#ifdef WIN32_MUTEX_IMPL_CRITICAL_SECTION
		typedef CRITICAL_SECTION	MutexHandle;
	#endif
	#ifdef WIN32_MUTEX_IMPL_SWRLOCK
		typedef SRWLOCK				MutexHandle;
	#endif
#else // PTHREAD 
    typedef pthread_mutex_t		MutexHandle;
#endif

	//--------------------------------------------------------------------------------------------------------------------	
	class Mutex
    {
    public:
        inline Mutex( void );
        inline ~Mutex( void );

		/// Try to lock (only 1 try) - returns true if successful 
        inline bool		TryLock( void );
		/// Lock (infinite retries)
        inline void		Lock( void );
		/// Unlock 
        inline void		Unlock( void );
    private:

        MutexHandle        mHandle;
		
		friend class ConditionVariable;
    };


#ifdef WIN32

	//----------------------------------------------------------------
	//----------------------------------------------------------------
    Mutex::Mutex( void )
    : mHandle( /*INVALID_MUTEX_HANDLE*/ )
    {
		

	#ifdef WIN32_MUTEX_IMPL_OLD_MUTEX
		mHandle = CreateMutex(	NULL,   // default security attributes
								FALSE,	// initially not owned
					            NULL);  // unnamed mutex
	#endif
		 
	#ifdef WIN32_MUTEX_IMPL_CRITICAL_SECTION
		InitializeCriticalSection( & mHandle );
	#endif

	#ifdef WIN32_MUTEX_IMPL_SWRLOCK
		InitializeSRWLock( & mHandle );
	#endif
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    Mutex::~Mutex( void )
    {
	#ifdef WIN32_MUTEX_IMPL_OLD_MUTEX
        INT32 iResult = CloseHandle( mHandle );
        MB_ASSERT( 0 != iResult );
	#endif
		 
	#ifdef WIN32_MUTEX_IMPL_CRITICAL_SECTION
		DeleteCriticalSection( & mHandle );
	#endif

	#ifdef WIN32_MUTEX_IMPL_SWRLOCK
		// NOTHING
	#endif
    }

	//----------------------------------------------------------------
	//----------------------------------------------------------------
    bool Mutex::TryLock( void )
    {
	#ifdef WIN32_MUTEX_IMPL_OLD_MUTEX
		// NO AVAILABLE IMPLEMENTATION
		return false;
		//INT32 iResult = WaitForSingleObject( mHandle, INFINITE );
		//MB_ASSERT( WAIT_OBJECT_0 == iResult );
	#endif
		 
	#ifdef WIN32_MUTEX_IMPL_CRITICAL_SECTION
		return 0 != TryEnterCriticalSection( & mHandle );
	#endif

	#ifdef WIN32_MUTEX_IMPL_SWRLOCK
		return 0 != TryAcquireSRWLockExclusive( & mHandle );
	#endif

		
    }

	//----------------------------------------------------------------
	//----------------------------------------------------------------
    void Mutex::Lock( void )
    {
	#ifdef WIN32_MUTEX_IMPL_OLD_MUTEX
		INT32 iResult = WaitForSingleObject( mHandle, INFINITE );
		MB_ASSERT( WAIT_OBJECT_0 == iResult );
	#endif
		 
	#ifdef WIN32_MUTEX_IMPL_CRITICAL_SECTION
		EnterCriticalSection( & mHandle );
	#endif

	#ifdef WIN32_MUTEX_IMPL_SWRLOCK
		AcquireSRWLockExclusive( & mHandle );
	#endif 
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    void Mutex::Unlock( void )
    {	
	#ifdef WIN32_MUTEX_IMPL_OLD_MUTEX
		BOOL iResult = ReleaseMutex( mHandle );
		MB_ASSERT( 0 != iResult );
	#endif
		 
	#ifdef WIN32_MUTEX_IMPL_CRITICAL_SECTION
		LeaveCriticalSection( & mHandle );
	#endif

	#ifdef WIN32_MUTEX_IMPL_SWRLOCK
		ReleaseSRWLockExclusive( & mHandle );
	#endif  
    }

#else // PTHREAD 

	//----------------------------------------------------------------
	//----------------------------------------------------------------
	Mutex::Mutex( void )
    : mHandle( /*INVALID_MUTEX_HANDLE*/ )
    {
        INT32 iResult = pthread_mutex_init( & mHandle, nullptr );
        MB_ASSERT( 0 == iResult );
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    Mutex::~Mutex( void )
    {
        INT32 iResult = pthread_mutex_destroy( & mHandle );
        MB_ASSERT( 0 == iResult );
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    bool Mutex::TryLock( void )
    {
        return 0 == pthread_mutex_trylock( &mHandle );
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    void Mutex::Lock( void )
    {
        INT32 iResult = pthread_mutex_lock( &mHandle );
        MB_ASSERT( 0 == iResult );
    }
	//----------------------------------------------------------------
	//----------------------------------------------------------------
    void Mutex::Unlock( void )
    {
        INT32 iResult = pthread_mutex_unlock( &mHandle );
        MB_ASSERT( 0 == iResult );
    }

#endif
}// namespace Parallel 
 
}// namespace Melting